package com.kelvem.saas.workbanch.fsm.aspect;

import com.kelvem.saas.workbanch.fsm.anno.Fsm;
import com.kelvem.saas.workbanch.fsm.anno.event.RegisterEvent;
import com.kelvem.saas.workbanch.fsm.container.FsmContainer;
import com.kelvem.saas.workbanch.fsm.core.EventContext;
import com.kelvem.saas.workbanch.fsm.handle.Handle;
import com.kelvem.saas.workbanch.fsm.handle.HandleExecutor;
import com.kelvem.saas.workbanch.fsm.model.EventNode;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.List;

/**
 * 注解执行时拦截
 *
 * @author kelvem
 */
@Aspect
@Component
@Slf4j
public class EventAspect {
    
    @Resource
    private HandleExecutor handleExecutor;
    
    /**
     * 定义一个切入点
     */
    @Pointcut("@annotation(com.kelvem.saas.workbanch.fsm.anno.event.RegisterEvent)")
    public void annotationPointCut(){
        // do nothing
    }
    
    @Before("annotationPointCut()")
    public void doBefore(JoinPoint joinPoint) {
    
        // 清空数据, 防止线程池混淆
        EventContext.init();
        
        // eventNode
        EventNode eventNode = this.getEventNode(joinPoint);
        EventContext.setEventNode(eventNode);
        
        int argsLen = joinPoint.getArgs().length;
        if (argsLen > 1) {
            throw new RuntimeException("Event[" + eventNode.getCode() + "]只允许最多有一个参数");
        }
        
        if (argsLen == 1) {
            Object req = joinPoint.getArgs()[0];
            log.info("Event[" + eventNode.getCode() + "] req=" + req);
            EventContext.setRequest(req);
        }
        
    }
    
    @After("annotationPointCut()")
    public void doAfter(JoinPoint joinPoint) {
    
        // eventNode
        EventNode eventNode = this.getEventNode(joinPoint);
    
        // execute handle list
        List<Class<? extends Handle>> beforeHandleList = eventNode.getBeforeHandleList();
        for (Class<? extends Handle> handle:
                beforeHandleList) {
            this.handleExecutor.sync(handle);
        }
        List<Class<? extends Handle>> handleList = eventNode.getHandleList();
        for (Class<? extends Handle> handle:
                handleList) {
            this.handleExecutor.sync(handle);
        }
        List<Class<? extends Handle>> afterHandleList = eventNode.getAfterHandleList();
        for (Class<? extends Handle> handle:
                afterHandleList) {
            this.handleExecutor.sync(handle);
        }
        
    }
    
    private EventNode getEventNode(JoinPoint joinPoint) {
    
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        Class<?> targetClazz = methodSignature.getDeclaringType();

        Fsm fsmAnno = targetClazz.getAnnotation(Fsm.class);
        Class entityClazz = fsmAnno.entityClazz();
        RegisterEvent registerEventAnno = method.getAnnotation(RegisterEvent.class);
        String eventCode = registerEventAnno.eventCode();

        EventNode eventNode = FsmContainer.getEvent(entityClazz, eventCode);
        return eventNode;
    }
}
